import React, { useState, useEffect } from 'react';
import {
  TextField,
  IconButton,
  Chip,
  GridLegacy as Grid,
  Box,
} from '@mui/material';
import { Add } from '@mui/icons-material';

interface ListComponentProps {
  id?: string;
  model?: string;
  realType?: string;
  label?: string;
  disabled?: boolean;
  value?: any;
  default?: any;
  onChange?: (event: any, index?: any, value?: any) => void;
  [key: string]: any;
}

interface ListComponentState {
  model?: string;
  children: any[] | Record<string, any>;
  newItemValue: string;
  newItemErrorText?: string;
  value?: any;
}

const ListComponent: React.FC<ListComponentProps> = ({
  id,
  model,
  realType = 'list',
  label,
  disabled = false,
  value: propValue,
  default: defaultProp,
  onChange,
  ...otherProps
}) => {
  const isDict = (): boolean => {
    return realType === 'dict' || realType === 'dict(dict)';
  };

  const [state, setState] = useState<ListComponentState>({
    model,
    children: isDict() ? {} : [],
    newItemValue: '',
    newItemErrorText: undefined,
  });

  useEffect(() => {
    if (defaultProp && isDict() && propValue === '') {
      // Handle default value if needed
    }
  }, [defaultProp, propValue, isDict]);

  const isChildValidListOfDict = (value: string): boolean => {
    const validBracket = value[0] === '[' && value[value.length - 1] === ']';
    if (!validBracket) {
      return false;
    }
    try {
      const processedValue = value.replace(/'([^']+)':/g, '"$1":').replace(/'/g, '"');
      const valueArray = JSON.parse(processedValue);
      let valid = true;
      valueArray.forEach((element: any) => {
        if (!isChildValidDict(JSON.stringify(element))) {
          valid = false;
        }
      });
      return valid;
    } catch (e) {
      return false;
    }
  };

  const isChildValidDict = (value: string): boolean => {
    const validBraces = value.indexOf('{') === 0 && value.lastIndexOf('}') === value.length - 1;
    if (!validBraces) {
      return false;
    }

    try {
      const processedValue = value.replace(/'([^']+)':/g, '"$1":').replace(/'/g, '"');
      JSON.parse(processedValue);
      return true;
    } catch (e) {
      return false;
    }
  };

  const isValid = (value: string): boolean => {
    switch (realType) {
      case 'list(float)':
        return /^-?\d*(\.\d+)?$/.test(value);
      case 'list(list(float))':
        if (value.endsWith(',')) return false;
        return value.split(',').every((element) => /^-?\d*(\.\d+)?$/.test(element.trim()));
      case 'dict':
        const colonCount = (value.match(/:/g) || []).length;
        return colonCount === 1 && !value.startsWith(':') && !value.endsWith(':');
      case 'dict(dict)': {
        const cleanedValue = value.replace(/[ "']/g, '');
        if ((cleanedValue.match(/;/g) || []).length !== 0) return false;
        const openBraces = (cleanedValue.match(/{/g) || []).length;
        const closeBraces = (cleanedValue.match(/}/g) || []).length;
        if (openBraces !== 1 || closeBraces !== 1) return false;
        if (cleanedValue.indexOf('{') > cleanedValue.indexOf('}') || !cleanedValue.endsWith('}')) {
          return false;
        }
        const match = cleanedValue.match(/\{(.*?)\}/);
        if (!match) return false;
        const subDict = match[1];
        const subColons = (subDict.match(/:/g) || []).length - 1;
        const subCommas = (subDict.match(/,/g) || []).length;
        if (subColons !== subCommas) return false;
        return subDict.split(',').every((element) => {
          const elementColons = (element.match(/:/g) || []).length;
          return elementColons === 1 && !element.startsWith(':') && !element.endsWith(':');
        });
      }
      case 'list(dict)':
        return isChildValidListOfDict(value) || isChildValidDict(value);
      default:
        return true;
    }
  };

  const getErrorMessage = (): string => {
    switch (realType) {
      case 'list(float)':
        return 'Only float numbers are allowed.';
      case 'list(list(float))':
        return 'Only comma separated float numbers are allowed.';
      case 'dict':
        return 'Key:Value pairs must be separated by colon : ';
      case 'dict(dict)':
        return 'Incorrect format. Example -> v_soma : { sec: soma, loc: 0.5, var: v}';
      case 'list(dict)':
        return "Incorrect format. Example -> {'cellLabel':'gs21', 'x': 2}, or [{...}]";
      default:
        return 'Not a valid value';
    }
  };

  const handleNewItemChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
    setState({
      ...state,
      newItemValue: event.target.value,
      newItemErrorText: undefined,
    });
  };

  const addChild = (): void => {
    if (state.newItemValue !== '' && isValid(state.newItemValue)) {
      const newChildren = Array.isArray(state.children) ? [...state.children] : { ...state.children };

      switch (realType) {
        case 'list(list(float))': {
          const newValue = `[${state.newItemValue}]`;
          if (Array.isArray(newChildren)) {
            newChildren.push(newValue);
          }
          break;
        }
        case 'dict': {
          const parts = state.newItemValue.split(':').map((entry) => entry.trim());
          let newValue: any = parts[1];
          if (!isNaN(parseFloat(parts[1]))) {
            newValue = parseFloat(parts[1]);
          }
          (newChildren as Record<string, any>)[parts[0]] = newValue;
          break;
        }
        case 'dict(dict)': {
          const key = state.newItemValue.split(':')[0].replace(/[ "']/g, '');
          (newChildren as Record<string, any>)[key] = {};
          const match = state.newItemValue.match(/\{(.*?)\}/);
          if (match) {
            const subDict = match[1]
              .replace(/[ "']/g, '')
              .split(',')
              .map((entry) => entry.split(':'));
            subDict.forEach((entry) => {
              let val: any = entry[1];
              if (!isNaN(parseFloat(entry[1]))) {
                val = parseFloat(entry[1]);
              }
              (newChildren as Record<string, any>)[key][entry[0]] = val;
            });
          }
          break;
        }
        case 'list(dict)': {
          let newValue = state.newItemValue;
          if (isChildValidListOfDict(newValue)) {
            newValue = newValue.replace(/'([^']+)':/g, '"$1":').replace(/'/g, '"');
            const valueArray = JSON.parse(newValue);
            if (Array.isArray(newChildren)) {
              valueArray.forEach((element: any) => {
                newChildren.push(element);
              });
            }
          } else if (isChildValidDict(newValue)) {
            newValue = newValue.replace(/'([^']+)':/g, '"$1":').replace(/'/g, '"');
            const parsedNewValue = JSON.parse(newValue);
            if (Array.isArray(newChildren)) {
              newChildren.push(parsedNewValue);
            }
          }
          break;
        }
        default:
          if (Array.isArray(newChildren)) {
            newChildren.push(state.newItemValue);
          }
          break;
      }

      convertToPython(newChildren);
    } else {
      setState({
        ...state,
        newItemErrorText: getErrorMessage(),
      });
    }
  };

  const removeChild = (childIndex: string | number): void => {
    const newChildren = Array.isArray(state.children) ? [...state.children] : { ...state.children };

    if (realType === 'dict' || realType === 'dict(dict)') {
      delete (newChildren as Record<string, any>)[childIndex];
    } else if (Array.isArray(newChildren)) {
      newChildren.splice(Number(childIndex), 1);
    }

    convertToPython(newChildren);
  };

  const convertToPython = (children: any[] | Record<string, any>): void => {
    setState({
      ...state,
      children,
      newItemValue: '',
    });

    let newValue: any;

    if (realType === 'dict' || realType === 'dict(dict)') {
      newValue = children;
    } else if (Array.isArray(children)) {
      newValue = children.map((child) => {
        switch (realType) {
          case 'list(float)':
            return parseFloat(child);
          case 'list(list(float))':
            return JSON.parse(child);
          case 'list(dict)':
            return child;
          default:
            return child;
        }
      });
    } else {
      newValue = children;
    }

    if (newValue !== undefined && state.value !== newValue && onChange) {
      onChange(null, null, newValue);
    }
  };

  const convertFromPython = (
    prevValue: any,
    currentValue: any
  ): any[] | Record<string, any> | undefined => {
    if (currentValue !== undefined && prevValue !== currentValue && currentValue !== '') {
      if (realType === 'dict' || realType === 'dict(dict)') {
        return typeof currentValue === 'string' ? JSON.parse(currentValue) : currentValue;
      }
      if (realType === 'list(dict)') {
        return typeof currentValue === 'string' ? JSON.parse(currentValue) : currentValue;
      }
      if (!Array.isArray(currentValue)) {
        return currentValue.map((child: any) =>
          typeof child === 'string' ? child : JSON.stringify(child)
        );
      }
      return currentValue.map((child: any) =>
        typeof child === 'string' ? child : JSON.stringify(child)
      );
    }
    return undefined;
  };

  useEffect(() => {
    const newValue = convertFromPython(undefined, propValue);
    if (newValue !== undefined) {
      setState((prevState) => ({
        ...prevState,
        children: newValue,
      }));
    }
  }, [propValue]);

  const childrenWithExtraProp = Object.keys(state.children).map((key, index) => {
    const keyStr = key.toString();
    let displayValue: string;

    if (realType === 'dict') {
      displayValue = `${keyStr} : ${JSON.stringify((state.children as Record<string, any>)[keyStr])}`;
    } else if (realType === 'dict(dict)') {
      displayValue = `${keyStr}:   ${JSON.stringify((state.children as Record<string, any>)[keyStr])
        .replace(/["']/g, '')
        .replace(/[:]/g, ': ')
        .replace(/[,]/g, ', ')}`;
    } else if (realType === 'list(dict)') {
      displayValue = `${JSON.stringify((state.children as any[])[Number(keyStr)])
        .replace(/["']/g, '')
        .replace(/[:]/g, ': ')
        .replace(/[,]/g, ', ')}`;
    } else {
      displayValue = (state.children as any[])[Number(keyStr)];
    }

    return (
      <Chip
        key={keyStr}
        label={displayValue}
        sx={{ margin: '0 8px 8px 0' }}
        onDelete={() => removeChild(keyStr)}
        color="primary"
      />
    );
  });

  return (
    <Box>
      <Grid container spacing={1} alignItems="flex-end">
        <Grid item>
          <Box
            display="flex"
            alignItems="center"
            sx={{
              backgroundColor: 'rgba(255, 255, 255, 0.09)',
              borderTopLeftRadius: '4px',
              height: '55.09px',
              borderBottom: '1px solid rgba(255, 255, 255, 0.7)',
            }}
          >
            <IconButton
              id={`${id}-button`}
              onClick={addChild}
              disabled={disabled}
              sx={{ height: 48 }}
            >
              <Add />
            </IconButton>
          </Box>
          <TextField
            variant="filled"
            disabled={disabled}
            id={id}
            label={label ? `Add new ${label}` : 'Add new item'}
            onChange={handleNewItemChange}
            onKeyPress={(e) => (e.key === 'Enter' ? addChild() : null)}
            value={state.newItemValue}
            fullWidth
            helperText={state.newItemErrorText}
            className="field-start-icon"
          />
        </Grid>
      </Grid>
      <Box m={1}>{childrenWithExtraProp}</Box>
    </Box>
  );
};

export default ListComponent;

